# Copyright (c) HySoP 2011-2024
#
# This file is part of HySoP software.
# See "https://particle_methods.gricad-pages.univ-grenoble-alpes.fr/hysop-doc/"
# for further info.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Interface to numpy arrays, with hysop predifined types for int, real ...
Those functions are useful to enforce hysop predefined types in numpy arrays.
"""
from hysop.tools.htypes import check_instance
import numpy as npw
##########################
### EXTRA HYSOP METHODS ##
def __generate_hysop_type_functions():
functions = {
"as{type}array": '''
def hysop_array_generated_method(a, order=HYSOP_ORDER, **kargs):
"""
Convert the input to an array of dtype HYSOP_{TYPE}.
"""
dtype = HYSOP_{TYPE}
return np.asarray(a=a, dtype=dtype, order=order, **kargs)
''',
"asany{type}array": '''
def hysop_array_generated_method(a, order=HYSOP_ORDER, **kargs):
"""
Convert the input to an array of dtype HYSOP_{TYPE}.
"""
dtype = HYSOP_{TYPE}
return np.asanyarray(a=a, dtype=dtype, order=order, **kargs)
''',
"{type}_prod": '''
def hysop_array_generated_method(a, axis=None, out=None, **kargs):
"""
Sum of array elements over a given axis.
"""
dtype = HYSOP_{TYPE}
return np.prod(a=a,axis=axis,out=out,dtype=dtype,**kargs)
''',
"{type}_sum": '''
def hysop_array_generated_method(a, axis=None, out=None, **kargs):
"""
Sum of array elements over a given axis.
"""
dtype = HYSOP_{TYPE}
return np.sum(a=a,axis=axis,out=out,dtype=dtype,**kargs)
''',
"{type}_empty": '''
def hysop_array_generated_method(shape, order=HYSOP_ORDER, **kargs):
"""
Return a new array of given shape and type, without initializing entries.
"""
dtype = HYSOP_{TYPE}
return np.empty(shape=shape, dtype=dtype, order=order, **kargs)
''',
"{type}_ones": '''
def hysop_array_generated_method(shape, order=HYSOP_ORDER, **kargs):
"""
Return a new array of given shape filled with ones of type HYSOP_{TYPE}.
"""
dtype = HYSOP_{TYPE}
return np.ones(shape=shape, order=order, dtype=dtype, **kargs)
''',
"{type}_zeros": '''
def hysop_array_generated_method(shape, order=HYSOP_ORDER, **kargs):
"""
Return a new array of given shape, filled with zeros of type HYSOP_{TYPE}.
"""
dtype = HYSOP_{TYPE}
return np.zeros(shape=shape, order=order, dtype=dtype, **kargs)
''',
"{type}_full": '''
def hysop_array_generated_method(shape, fill_value, order=HYSOP_ORDER, **kargs):
"""
Return a new array of given shape, filled with fill_value of type HYSOP_{TYPE}.
"""
dtype = HYSOP_{TYPE}
return np.full(shape=shape, fill_value=fill_value, order=order, dtype=dtype, **kargs)
''',
}
hysop_types = ["real", "complex", "integer", "index", "dim", "bool"]
for ht in hysop_types:
for _fname, fdefinition in functions.items():
fname = _fname.format(type=ht, TYPE=ht.upper())
fdef = """
import numpy as np
from hysop.constants import HYSOP_REAL, HYSOP_COMPLEX, HYSOP_ORDER
from hysop.constants import HYSOP_INTEGER, HYSOP_INDEX, HYSOP_DIM, HYSOP_BOOL
{}
""".format(
fdefinition.format(type=ht, TYPE=ht.upper())
)
namespace = dict()
exec(fdef, namespace)
setattr(npw, fname, namespace["hysop_array_generated_method"])
if ht == "integer":
fname = _fname.format(type="int")
setattr(npw, fname, namespace["hysop_array_generated_method"])
__generate_hysop_type_functions()
[docs]
def ndindex_with_ghosts(shape, ghosts):
check_instance(shape, tuple)
check_instance(ghosts, (list, tuple, npw.ndarray))
if not isinstance(ghosts[0], (list, tuple, npw.ndarray)):
ghosts = (ghosts,)
for idx in npw.ndindex(*shape):
ghosts_idx = tuple(
tuple(i + g for (i, g) in zip(idx, ghost)) for ghost in ghosts
)
yield (idx,) + ghosts_idx
[docs]
def slices_empty(slices, shape):
if not __debug__:
return False
if slices is Ellipsis:
return False
from hysop.core.arrays.array import Array
if isinstance(slices, (int, npw.integer, npw.ndarray, Array)):
return
slices = (slices,) if isinstance(slices, slice) else slices
assert len(shape) >= len(slices)
shape = shape[: len(slices)]
empty = tuple(
slices[i].indices(shape[i])
for i in range(len(slices))
if isinstance(slices[i], slice)
)
empty = tuple((i >= j) for (i, j, _), ss in zip(empty, shape))
return any(empty)
[docs]
def set_readonly(*args):
from hysop.testsenv import __HAS_OPENCL_BACKEND__
from hysop.core.arrays.all import Array
if __HAS_OPENCL_BACKEND__:
from hysop.core.arrays.all import OpenClArray
for arg in args:
if __HAS_OPENCL_BACKEND__ and isinstance(arg, OpenClArray):
continue
if isinstance(arg, Array):
arg = arg.handle
arg.setflags(write=False)
[docs]
def fancy_print(
a,
replace_values={},
replace_views={},
element_width=6,
inital_val=None,
**print_opts,
):
"""
Print values with ghosts replaced by symbol.
Mainly for debug purposes.
Parameters
----------
a: np.ndarray
Array to be printed.
replace_values: dict, optional
Replace value matching key predicate by value in array.
Predicates take the array as parameter.
Values can be any printable object.
replace_views: dict, optional
Replace each key view by value in array.
Values can be any printable object.
element_width: int, optional
String width of the printed array values.
inital_val: object, optional
Initially fill the new array with given val.
If specified, new array won't be initialized by input array.
print_opts:
Numpy printing options.
Default options are:
threshold = 10k
linewidth = 1k
nanstr = 'nan'
infstr = 'inf'
formatter = custom formatter
"""
check_instance(a, npw.ndarray)
strarr = npw.empty_like(a, dtype=object)
if inital_val is None:
strarr[...] = a
else:
strarr[...] = inital_val
for predicate, replace_val in replace_values.items():
assert callable(predicate)
pred = predicate(a)
strarr[pred] = replace_val
for view, replace_val in replace_views.items():
strarr[view] = replace_val
_formatter = {
object: lambda x: "{:^{width}}".format(x, width=element_width)[:element_width],
float: lambda x: "{:{width}.2f}".format(x, width=element_width),
}
_print_opts = dict(
threshold=10000,
linewidth=1000,
nanstr="nan",
infstr="inf",
formatter={"object": lambda x: _formatter.get(type(x), _formatter[object])(x)},
)
_print_opts.update(print_opts)
from hysop.tools.contexts import printoptions
with printoptions(**_print_opts):
print(strarr)
npw.set_readonly = set_readonly
npw.ndindex_with_ghosts = ndindex_with_ghosts
npw.fancy_print = fancy_print